import { Request, Response, NextFunction } from 'express';
import { AuthenticatedRequest } from '../types';
import OperationLogModel from '../models/operationLogModel';

interface LogData {
  userId: string;
  action: string;
  details: string;
  route: string;
  method: string;
  params?: Record<string, unknown>;
  query?: Record<string, unknown>;
  body?: Record<string, unknown>;
  userAgent?: string;
  ip?: string;
  responseStatus?: number;
  responseTime?: number;
  error?: string;
}

// 异步日志写入队列
class LogQueue {
  private static instance: LogQueue;
  private queue: LogData[] = [];
  private isProcessing = false;
  private readonly batchSize = 100;
  private readonly processInterval = 5000; // 5秒处理一次

  private constructor() {
    this.startProcessing();
  }

  public static getInstance(): LogQueue {
    if (!LogQueue.instance) {
      LogQueue.instance = new LogQueue();
    }
    return LogQueue.instance;
  }

  public async addLog(logData: LogData): Promise<void> {
    this.queue.push(logData);
  }

  private async processLogs(): Promise<void> {
    if (this.isProcessing || this.queue.length === 0) return;

    try {
      this.isProcessing = true;
      const batch = this.queue.splice(0, this.batchSize);
      
      await OperationLogModel.insertMany(batch, { ordered: false });
    } catch (error) {
      console.error('Error processing operation logs:', error);
    } finally {
      this.isProcessing = false;
    }
  }

  private startProcessing(): void {
    setInterval(() => {
      this.processLogs();
    }, this.processInterval);
  }
}

// 日志清理服务
class LogCleaner {
  private static readonly retentionDays = 90; // 保留90天的日志

  public static async cleanOldLogs(): Promise<void> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - this.retentionDays);

    try {
      await OperationLogModel.deleteMany({
        timestamp: { $lt: cutoffDate }
      });
    } catch (error) {
      console.error('Error cleaning old logs:', error);
    }
  }
}

// 启动定时清理任务
setInterval(() => {
  LogCleaner.cleanOldLogs();
}, 24 * 60 * 60 * 1000); // 每24小时执行一次

// 敏感数据过滤
const sanitizeData = (data: Record<string, any>): Record<string, any> => {
  const sensitiveFields = ['password', 'token', 'secret', 'creditCard'];
  const sanitized = { ...data };

  for (const field of sensitiveFields) {
    if (field in sanitized) {
      sanitized[field] = '******';
    }
  }

  return sanitized;
};

// 操作日志中间件
export const operationLogMiddleware = async (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  const startTime = Date.now();
  const user = (req as AuthenticatedRequest).user;

  // 如果未登录用户，跳过日志记录
  if (!user?.userId) {
    return next();
  }

  // 保存原始end方法
  const originalEnd = res.end;
  let responseBody: any;

  // 重写end方法以捕获响应数据
  res.end = function(chunk: any, ...args: any[]): any {
    if (chunk) {
      responseBody = chunk.toString();
    }
    return originalEnd.apply(res, [chunk, ...args]);
  };

  try {
    // 等待请求处理完成
    await new Promise<void>((resolve) => {
      res.on('finish', async () => {
        const logData: LogData = {
          userId: user.userId,
          action: req.method,
          details: `${req.method} ${req.originalUrl}`,
          route: req.originalUrl,
          method: req.method,
          params: sanitizeData(req.params),
          query: sanitizeData(req.query),
          body: sanitizeData(req.body),
          userAgent: req.headers['user-agent'],
          ip: req.ip,
          responseStatus: res.statusCode,
          responseTime: Date.now() - startTime
        };

        // 如果是错误响应，记录错误信息
        if (res.statusCode >= 400) {
          try {
            const errorData = JSON.parse(responseBody);
            logData.error = errorData.message || 'Unknown error';
          } catch {
            logData.error = responseBody || 'Unknown error';
          }
        }

        // 异步写入日志队列
        await LogQueue.getInstance().addLog(logData);
        resolve();
      });
    });

    next();
  } catch (error) {
    console.error('Error in operation log middleware:', error);
    next();
  }
};
